/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * bocciawii
 * Copyright (C) Grupo de trabajo ULE 2010 <ulewii@googlegroups.com>
 * 
 * bocciawii is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * bocciawii is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _INPUT_H_
#define _INPUT_H_

#ifndef _CAMARA_H_
#define N 1
#define S 2
#define E 4
#define O 8
#define NE 5
#define NO 9
#define SE 6
#define SO 10
#define ZOOMMAS 109
#define ZOOMMENOS 110
#endif

#include <iostream>
#include <SDL/SDL.h>
#include <pthread.h>
#include <wiiuse.h>
#include <cstdlib>
#include <cmath>

#define MAX_WIIMOTES 1

#define ACC_NINGUNA 0
#define ACC_SELECCIONAR 1
#define ACC_SALIR 16
#define ACC_MIRAR 32

#define EST_TRANSICION 1
#define EST_MENU 2
#define EST_JUEGO 4
#define EST_OBSERVACION 8
#define EST_TACTICA 16
#define EST_ANIMACION 32
#define EST_INGAME 4+8+16
#define EST_PREPROCESO_LANZAMIENTO 64
#define EST_LANZAMIENTO 128
#define EST_LOGICA 256

#define BOT_WM_UNO 0
#define BOT_WM_DOS 1
#define BOT_WM_IZQ 2
#define BOT_WM_DER 3
#define BOT_WM_ARR 4
#define BOT_WM_ABA 5
#define BOT_WM_A   6
#define BOT_WM_B   7
#define BOT_WM_MAS 8
#define BOT_WM_MEN 9
#define BOT_WM_HOME 10

/**
 * @brief Estructura para el cursor en el evento
 *
 * Simple, visible (si hay dos puntos de ir), x e y
 */
typedef struct
{
	int x;
	int y;
	int visible;
} cursor;



/**
 * @brief Estructura para un evento de resize
 *
 * Obsoleto No habrá resize, ni cambio entre fullscreen y ventana
 */
typedef struct
{
	int width;
	int height;
} resize_params;


/**
 * @brief Estructura para almacenamiento de datos wiimote
 *
 * Campos para los ángulos
 */
typedef struct
{
	float roll;
	float pitch;
	float yaw;
	float raw_roll;
	float raw_pitch;
	float raw_yaw;
} rotacion;


/**
 * @brief Estructura para almacenamiento de datos wiimote
 *
 * Campos para acelerómetros
 */
typedef struct
{
	float x;
	float y;
	float z;
	float raw_x;
	float raw_y;
	float raw_z;
} acel_vel;


/**
 * @brief Estructura para "miradas"
 *
 * Se exporta desde el Poll directamente una mirada que entienden las cámaras
 */
typedef struct
{
	int evento;
	int factor_x;
	int factor_y;
} desplazamiento;


/**
 * @brief Estructura del evento
 *
 * Evento que será devuelto en los polls. Mezclará SDL (debug y gestión) y WM
 */
typedef struct
{
	int desde;
	int id;
	cursor pun;
	resize_params res;
	rotacion rot;
	acel_vel acel;
	acel_vel vel;
	rotacion rotwm;
	desplazamiento des;
	int id_elemento;
	
} accion_aplicacion;

/**
 * @brief Estructura de un evento de WM
 *
 * Se usará en el poll al wiimote.
 * Los botones no nos importan en la aplicación, así que para qué tenerlos?
 * Se mapearán al teclado, como teclas de SDL. Empezó así como DEBUG, pero ha terminado
 * siendo bastante útil, sencillo y directo
 */
typedef struct
{
	acel_vel acel;
	acel_vel vel;
	rotacion rot;
	rotacion rotwm;
	cursor pun;
	Uint8 just_pressed[11];
	Uint8 held[11];
	Uint8 just_released[11];
	Uint8 pressed[11];
} evento_wm;


/**
 * @brief Traducción entre evento y acción
 * 
 * Esta clase se encargará de hacer Poll tanto a SDL como al Wiiremote.
 * 
 * La clase ofrecerá una traducción entre un evento puro (p. ej. se ha pulsado un botón)
 * y una acción que el usuario querrá realizar. Esto es, nos ofrece una abstracción
 * bastante útil y necesaria en la aplicación, ya que además utiliza el estado de la
 * aplicación para realizar la traducción, por lo que el código en el bucle principal
 * queda más limpio y directo (hay tantos callbacks que sería demasiado meterle encima)
 * el tratamiento que se hace en el poll.
 *
 * Existen tres acciones en nuestra aplicación. Salir (proveniente únicamente de SDL, al pinchar
 * en la crucecita de cerrar la aplicación), 
 * Mirar, y Seleccionar. Gracias a lo expuesto arriba acerca de mapear botones a teclado
 * se han podido simplificar las acciones a realizar en estas únicamente.
 *
 * La acción ACC_SALIR es producida por un evento de SDL al pinchar sobre la cruz 
 * para cerrar la aplicación o al presionar ALT+F4.
 * 
 * La acción ACC_SELECCIONAR es producida por cualquier evento KEYDOWN de SDL en DEBUG
 * o cualquier pulsación del mando. Dependiendo del estado todas acabarán siendo
 * mapeadas a una tecla, y esta tecla será la que se filtre en los callbacks de la aplicación
 * principal.
 * 
 * La acción ACC_MIRAR es producida por la pulsación de BOT_WM_A y el cursor visible del mando
 * o bien por un evento de SDL_BUTTONEVENT con button = left en DEBUG.
 *
 * Todos los eventos son devueltos en el contexto del estado de la aplicación.
 * Por ejemplo, pulsando A en un estado menú produce una selección de un elemento
 * pero la pulsación de A en un EST_INGAME produce una acción de mirada.
 */
class Input
{
public:

	/**
	 * @brief Nuestro constructor. Arranca el input con una resoluciónd e pantalla
	 *
	 * La resolución de pantalla será utilizada a posteriori para establecer la 
	 * pantalla virtual del Wiiremote
	 */
	Input(int x, int y);
	~Input();

	/**
	 * @brief Pone en marcha el hilo de conexión y poll a un wiimote
	 *
	 * Arranca el hilo de ejecución que se encarga de hacer poll al wiiremote.
	 * Este hilo quedará en ejecución a lo largo de la vida de la aplicación.
	 *
	 * El hilo busca wiimotes mientras no haya ninguno conectado
	 *
	 * El hilo hace poll al mando mientras haya mandos conectados
	 *
	 * El hilo se detiene cuando final_forever = 1;
	 *
	 * El poll se detiene cuando num_wiimotes = 0 o done_poll_wm = 1 (esto último)
	 * es para parar el poll cuando se conecta una extensión, aunque tal vez quede
	 * obsoleto.
	 *
	 * Así, si el mando se apaga (pulsando el botón de apagado)
	 * el hilo tratará de reconectarse a otro mando, pero el usuario no tendrá
	 * que realizar ninguna acción, tan sólo dejar el mando en descubrible 
	 * y tal vez reactivar los infrarrojos.
	 *
	 * Sacar el hilo de poll al wiimote era necesario para poder filtrar bien
	 * los eventos sin que se produjera ningún desfase entre los frames
	 * preparados y los que se están procesando. Si hubiéramos utilizado también
	 * eventos muy continuos de SDL como movimiento del ratón, también tendríamos
	 * que haberlo sacado a un hilo aparte para conseguir evitar este desfase.
	 * No se optó por hacer lo mismo con SDL ya que únicamente era útil a efectos
	 * de debug que pronto fueron prescindibles debido a la facilidad en el desarrollo
	 * con el wiimote.
	 */
	void Iniciar();
	
	/**
	 * @brief Poll al gestor de ventanas y al dispositivo de juego
	 *
	 * Se trata de un poll dependiente del contexto (<code>estado</code>).
	 * El procedimiento hará poll al gestor en el momento en el que se llame
	 * y mezclará este evento con el evento_WM (que está siendo modificado contínuamente
	 * por el hilo de ejecución del poll al wiimote).
	 *
	 * @param estado El identificador del estado en el que se encuentra la aplicación
	 * @return Acción a realizar
	 */
	accion_aplicacion Poll(int estado);

	
	/**
	 * @brief Activar una vibración durante un periodo de tiempo
	 *
	 * Era curioso implementarlo para cuando se realizase alguna acción concreta, 
	 * en el menú, por ejemplo.
	 *
	 * La vibración se desactiva cuando se comprueba en el Poll que ha pasado el 
	 * tiempo pedido
	 *
	 * @param duración milisegundos que el mando estará vibrando.
	 */
	void Vibra(int duracion);

	/**
	 * @brief Activa o desactiva los IR del mando
	 *
	 * El hilo de ejecución del wiimote activa por defecto acelerómetros y 
	 * y giroscopios de WM+. Activar los infrarrojos en un espacio tan corto de tiempo
	 * sin procesar los eventos correspondientes que se generasen entre ellos, 
	 * podría producir que los IR no se activasen correctamente. He ahí el por qué
	 * de este procedimiento.
	 *
	 * @param activado 1 para activar, 0 para desactivar
	 */
	void SetIR(int activado);
	
	/**
	 * @brief Determinar si hay mandos conectados
	 *
	 * Devuelve el número de mandos a los que está conectado Input. Actualmente
	 * sólo hay soporte para un mando (<code>MAX_WIIMOTES</code>).
	 *
	 * @return int con el número de conexiones
	 */
	inline int NumeroConexiones()
	{	return nro_conex;   }

	
	
protected:

private:
	// Parar y reiniciar poll
	void PararPoll(int duracion);
	void ReiniciarPoll();
	// Eventos vacíos
	accion_aplicacion evento_cero();
	void evento_wm_cero(evento_wm* e);
	// Procesamiento evento
	desplazamiento InterpretarMirada();


	// Calco de num_wiimotes
	int nro_conex;					
	// Eventos
	accion_aplicacion evento;
	accion_aplicacion evento_previo;
	SDL_Event evento_SDL;
	evento_wm evento_WM;
	// Resolución Virtual Screen
	int ancho, alto;
	aspect_t aspecto;
	ir_position_t posicion_barra;
	// Temporizadores para la vibración
	int _vibrando;
	long int _comienzoVibracion;
	long int _tiempoVibracion;
	// Temporizadores para el poll
	long int _paradaPoll;
	int _tiempoParadaPoll;
	// El hilo del wiimote
	pthread_t hilo;
	
	


};


#endif // _INPUT_H_

